package cash.cmp.personal_financial_system.config;

import cash.cmp.personal_financial_system.security.AccountRealm;
import cash.cmp.personal_financial_system.security.JWTFilter;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    /**
     * 安全管理器
     * @param accountRealm
     * @return
     */
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager securityManager(AccountRealm accountRealm){
        DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
        // 设置自定义 realm.
        securityManager.setRealm(accountRealm);

        //关闭session
        DefaultSubjectDAO subjectDAO=new DefaultSubjectDAO();

        DefaultSessionStorageEvaluator sessionStorageEvaluator=new DefaultSessionStorageEvaluator();
        sessionStorageEvaluator.setSessionStorageEnabled(false);
        subjectDAO.setSessionStorageEvaluator(sessionStorageEvaluator);
        securityManager.setSubjectDAO(subjectDAO);
        return securityManager;
    }

    /**
     * 过滤工厂
     * @param securityManager
     * @return
     * 先走 filter ，然后 filter 如果检测到请求头存在 token，则用 token 去 login，走 Realm 去验证
     */
    @Bean
    public ShiroFilterFactoryBean factory(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean factoryBean=new ShiroFilterFactoryBean();
        factoryBean.setSecurityManager(securityManager);
        // 添加自己的过滤器并且取名为jwt
        Map<String, Filter> filterMap=new LinkedHashMap<>();
        //设置我们自定义的JWT过滤器
        filterMap.put("jwt",new JWTFilter());
        //设置自定义的跨域过滤器
        filterMap.put("corsConfig", new CorsConfig());
        factoryBean.setFilters(filterMap);

        Map<String,String>filterRuleMap=new HashMap<>();
        // 所有请求通过我们自己的JWT Filter
        filterRuleMap.put("/**","jwt");

         /*
               anon：无需认证即可访问
               authc：必须认证才能访问
               user：必须拥有记住我功能才能访问
               perms：拥有对某个资源的权限才能访问
               role：拥有对某个角色权限才能访问
          */
        // 设置无权限时跳转的 url;
        factoryBean.setUnauthorizedUrl("/account/unauthorized/无权限");

        // 登录注册注销接口放开
        filterRuleMap.put("/account/login", "anon");
        filterRuleMap.put("/account/register", "anon");
        filterRuleMap.put("/account/logout", "anon");
        filterRuleMap.put("/account/sendCode","anon");
        filterRuleMap.put("/account/loginByWx","anon");
        filterRuleMap.put("/account/loginByApiPay","anon");
        filterRuleMap.put("/account/loginBySms","anon");
        filterRuleMap.put("/account/loginByWxAndOpenId","anon");
        filterRuleMap.put("/account/unauthorized/**","anon");
        filterRuleMap.put("/websocket/*", "anon");

        factoryBean.setFilterChainDefinitionMap(filterRuleMap);
        return factoryBean;
    }

    /**
     * 添加注解支持，如果不加的话很有可能注解失效
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }


    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){

        AuthorizationAttributeSourceAdvisor advisor=new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    /**
     * 交由 Spring 来自动地管理 Shiro-Bean 的生命周期
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

}
